home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Interactive Reference Guide
/
C-C++ Interactive Reference Guide.iso
/
c_ref
/
csource4
/
262_01
/
debug.c
< prev
next >
Wrap
Text File
|
1988-03-30
|
28KB
|
873 lines
/***************************************************************
* An Interactive Debugger for Q/C *
* *
* by Robert Ramey *
* October 1984 *
***************************************************************/
#define MAXLINE 81 /*maximum line of data display format */
#define MAXARGS 12 /*maximum number of arguments of a function */
/*to be displayed. note:floats and longs */
/*count as two arguments */
#define MAXDEPTH 24 /*maximum number of breakpoints in stack */
#define INSTWIDTH 3 /*width of "call debug" instruction */
#define MAXCOMDEPTH 4 /*maximum depth of inclusion of *.DBG files */
#define APRXSYMBOLS 100 /*approx number of symbols to be defined */
#define SYMSIZE 6 /*size of a symbol */
typedef int boolean;
typedef char *ADDRESS; /*This machine addresses to the byte */
static boolean trace = 0; /*trace toggle*/
#define dataseg (&trace) /*assumes trace is first data symbol */
#include <stdio.h>
#include "symdef.h" /*definitions for symbol table routines */
static
FILE *fp[MAXCOMDEPTH]; /*stack of included command files */
/*file pointer for terminal input */
static
int bcnt = 0, /*number of breakpoints to be skipped */
depth = 0, /*number of currently pending breakpoints */
fpdepth = 0; /*level of command file inclusion */
static
SYMBOLTABLE *symtbl; /*table of symbols and data */
static
char *fptr, format[MAXLINE]; /*format line */
typedef struct symtbl_entry {
struct symtbl_entry *slnxt, /*next symbol in set/reset list*/
*slprv, /*previous symbol in set/reset list*/
*slalpha; /*next symbol in alphabetic list */
ADDRESS address; /*address of symbol according to *.sym */
char *symbol, /*pointer to symbol itself */
*ptype, /*pointer to format of data or result */
*arg_format, /*pointer to format of arguments */
/*(null for data segment items */
saved_inst[INSTWIDTH + sizeof(int *)],
/*when break point is set, first instructions*/
/*are stored here and replaced with call to trap */
/*followed by address of this structure */
state;
/*bit 0 indicates if break point set or not */
/*bit 1 indicates if break point pending or not*/
} SYMTBL_ENTRY;
#define PENDING 2
#define SET 1
static
SYMTBL_ENTRY *entrylist = NULL, /*list of set breakpoints */
*pendlist = NULL, /*list of pending breakpoints */
*alphafirst = NULL, /*alphabetic list of symbols */
*alphalast = NULL; /*last symbol loaded */
static
struct { /* special stack for active breakpoints */
ADDRESS return_address; /* address of calling function */
ADDRESS *sp;
SYMTBL_ENTRY *stdata; /* currently active break point*/
} bpstack[MAXDEPTH];
char *getfmt();
SYMTBL_ENTRY *dactall();
SYMBOLENTRY *getsym();
void resume();
/***************************************************************
setup - this subroutine should be called at the beginning of
program execution. It permits initial use of commands to load
symbol tables and set breakpoints.
***************************************************************/
setup()
{
/* setup symbol table */
if(!(symtbl = symmk(sizeof(SYMTBL_ENTRY), APRXSYMBOLS))){
fputs("Not enough memory for symbol table\n", stderr);
exit(1);
}
/* open file to be used for debugger commands */
fp[0] = fopen("con:", "r");
ldtbl("DEBUG.SYM"); /*load symbol file */
comand(); /*accept initial debugger commands */
actall();/* activate entry point traps and continue */
}
/**************************************************************
trap - when a subroutine is entered this function is called.
Here is where program flow is traced and if a breakpoint encounterd
execution is suspended in order to process debugger commands
**************************************************************/
static void
trap(caller)
ADDRESS caller;
{
ADDRESS entrypt;
SYMTBL_ENTRY *stdata;
boolean all;
/* retieve entry point address. assumes that the subroutine */
/* entry point has been replaced with a "call trap" instruc- */
/* -tion there leaving on the stack a return address just */
/* beyond the entry point */
entrypt = *(&caller - 1) - INSTWIDTH;
/* following the "call trap" instruction at the subroutine */
/* entry the address of the symbol data structure is stored */
stdata = *(int *)(entrypt + INSTWIDTH);
if(depth == MAXDEPTH)
fputs("Too many pending breakpoints\n",stderr);
else{
/* store information on encounterd breakpoint on */
/* stack of currently pending subroutines */
bpstack[depth].stdata = stdata;
bpstack[depth].return_address = caller;
bpstack[depth].sp = &caller + 1;
/* restore original contents to the entry point to */
/* permit normal execution of subroutine to proceed */
/* note: this means the tracing will not show */
/* recurrsive subroutine calls */
dactbp(stdata);
/* remove entry point from list to be activated */
relink(stdata, &pendlist);
stdata->state ^= PENDING;
/* replace address of caller with address of resume */
/* so that resume will gain control when subroutine */
/* returns. This will permit resume to display */
/* return value of subroutine */
caller = resume; /* return for interrupted routine */
/* increment index into stack of pending entry points*/
++depth;
}
if((!bcnt && stdata->state & SET) || trace){
/* deactivate all entry point traps to permit */
/* normal use of library subroutines by the debugger */
dactall();
/* display subroutine name and its arguments */
/* assumes that arguments have been pushed on to */
/* stack before calling trapped subroutine */
dentry(&caller+1,stdata);
}
else
all = FALSE;
if(stdata->state & SET) /*if is a a break point */
if(!bcnt)
comand();
else
--bcnt;
/* if necessary reactivate all entry point traps */
if(all) actall();
/* continue execution by loading entry point on top of */
/* current return address */
*(&caller - 1) = entrypt; /* return to break point */
}
/****************************************************************
resume - recieves control when a trapped subroutine returns.
The object of this is to undo the changes done in trap and
permit display of value returned.
*****************************************************************/
static void
resume(){
#asm
push af ;save z flag
push hl ;save return value on stack
#endasm
if(trace){
/* display value returned by subroutine */
dactall();
dexit(bpstack[depth-1].stdata);
actall();
}
/* reactivate trapping of this subrouine */
actbp(bpstack[--depth].stdata);
relink(bpstack[depth].stdata, &entrylist);
bpstack[depth].stdata->state ^= PENDING;
bpstack[depth].return_address++; /*leave return address in HL*/
#asm
pop de ;recover subroutine return value
pop af ;recover flags
push de
ex (sp),hl ;leave returned original returned value in HL
;leave original return address on stack
#endasm
}
/****************************************************************
actall - activate trapping of all subroutine calls for all entry
points in the entry point list.
*****************************************************************/
static void
actall()
{
SYMTBL_ENTRY *stdata;
for(stdata = entrylist;stdata;stdata = stdata->slnxt)
actbp(stdata);
}
/*****************************************************************
dactall - dactivate trapping of all subroutines in the list of
entry points. This is necessary in order to permit the debugger
program to use library routines without trapping itself.
******************************************************************/
static
SYMTBL_ENTRY *
dactall(entrypt)
ADDRESS entrypt;
{
SYMTBL_ENTRY *stdata;
for(stdata = entrylist;stdata;stdata = stdata->slnxt){
dactbp(stdata);
}
}
/******************************************************************
dentry - display entry point with arguments on entering function
******************************************************************/
static void
dentry(stackpt, stdata)
ADDRESS stackpt;
SYMTBL_ENTRY *stdata;
{
char *p;
/* set up string for format of arguments */
if(!stdata->ptype)
/* if no format string has been specified */
p = "";
else
p = stdata->arg_format;
ddata(">%s", stackpt, p, stdata->symbol, 2);
}
/***************************************************************
dexit - display breakpoint with return value on exit of function
****************************************************************/
static void
dexit(stdata,retval)
SYMTBL_ENTRY *stdata;
int retval;
{
char c, *a, *p, *t;
long longval;
int *aptr;
/* isolate first part of format string to be used to display */
/* argument. */
if(!stdata->ptype){
p = "";
a = &c;
}
else{
p = stdata->ptype;
a = stdata->arg_format;
}
c = *a;
*a = '\0';
/* if a long is returned, get long value from special stack */
/* check to see if prototype is long or float */
for(t = p;*t != '%' && *t != NULL;++t);
if(*t){
switch(*++t){
default:
aptr = &retval;
break;
case 'l':
case 'L':
case 'f':
case 'F':
/* leave address of long value in HL */
aptr = &longval;
#asm
call ?save32;/* move TOS to longval */
#endasm
}
}
ddata("<%s = ", aptr, p, stdata->symbol, 2);
*a = c;
}
/*************************************************************
ddata - display data from memory
**************************************************************/
static void
ddata(idstr, data_address, ptype, symbol, charsize)
char *idstr, /*address of string identifying symbol */
*symbol, /*address of symbol to be displayed */
*ptype; /*points to format of display */
int data_address[],
/*points to first memory location to be displayed */
charsize; /*size of character variable */
/*1 if in memory, 2 if on a stack */
{
int arg[MAXARGS]; /*arguments to be displayed */
/*note: must be first for fprint (below) to function */
int i;
/*indent display according to depth of call stack */
for(i = 0;i < depth;++i) format[i] = ' ';
fptr = strmov(format+depth,idstr);
/*first item displayed is the symbol it self */
arg[0] = symbol;
/*fill rest of arg array */
farg(arg + 1, data_address, &ptype, charsize);
fptr = strmov(fptr, "\n");
fprintf(stderr,format); /*format is format string. */
/*arguments are on top of stack already */
/*because arg[] is first local variable */
}
/***************************************************************
farg - fill in array of arguments to be passed to fprintf.
Leave modifyed format string in static variable format.
**************************************************************/
static void
farg(arg, data_address, ptype, charsize)
int arg[], /* array into which arguments are to be loaded */
data_address[], /* array which currently holds arguments */
charsize; /* size of objects to be displayed with %c */
/* These are 1 if character is in memory, */
/* 2 if character has been pushed onto stack */
char **ptype; /* pointer to address of format string to be used */
{
int icount, *targ;
char c, state;
state = ' ';
icount = 0;
while(c = *(*ptype)++){
switch(state){
case ' ':
switch(c){
default:
icount = 0;
break;
case '%':
state = '%';
break;
case '*':
state = '*';
icount = 1;
break;
case '(':
*fptr++ = c;
farg(arg,data_address,ptype,charsize);
c = ')';
break;
case ')':
return;
case '\"':
state = '\"';
break;
}
break;
case '\"':
if(c == '\"')
state = ' ';
break;
case '*':
switch(c){
case '*':
++icount;
break;
case '%':
state = '%';
break;
case '(':
fptr -= icount;
targ = data_address++;
while(icount--)
targ = *targ;
*fptr++ = '(';
farg(arg,targ,ptype,1);
c = ')';
default:
state = ' ';
icount = 0;
break;
}
break;
case '%':
state = ' ';
if(c == '%' || c == ')' || c == '('){
fptr -= icount;
break;
}
if(c == 'c' && charsize == 1 && icount == 0){
targ = (char *)data_address;
data_address =
(int *)(1 + (char *)data_address);
}
else{
targ = data_address++;
while(icount--)
targ = *targ;
}
if(c == 'c' && charsize == 1)
*arg++ = *(char *)targ;
else
if(c == 'f' || c == 'l'){
/* longs and floats use two words */
*arg++ = *targ++;
*arg++ = *targ;
}
else
/* everything else uses one word */
*arg++ = *targ;
}
*fptr++ = c;
}
}
/*****************************************************************
comand - read and process command from keyboard or file
*****************************************************************/
static void
comand()
{
SYMBOLENTRY *ste;
SYMTBL_ENTRY *stdata;
char c, *fstart, *fend;
int addr, i;
FILE *fl;
for(;;){ /* main command loop */
putc('*',stderr); /* prompt screen for input */
/* get line from current input file */
while(!fgets(format,MAXLINE - 1,fp[fpdepth])){
if(!fpdepth) return;
--fpdepth;
continue;
}
if(!isatty(fileno(fp[fpdepth])))/*if input not from keyboard */
fputs(format,stderr); /*echo input to screen */
fstart = format;
while(isspace(*++fstart)); /*skip leading spaces */
switch(tolower(format[0])){
case 'b':/*toggle breakpoint */
/*b<breakpoint symbol> [<format string>]*/
/*b without arg toggles all breakpoints */
if(!*fstart){ /*no arguments*/
for(stdata = pendlist;stdata;
stdata = stdata->slnxt)
stdata->state ^= SET;
for(stdata = entrylist;stdata;
stdata = stdata->slnxt)
stdata->state ^= SET;
break;
}
/* with a symbol */
if(!(ste = getsym(&fstart))){
serr();
break;
}
stdata = (SYMTBL_ENTRY *)symdat(symtbl,ste);
if(stdata->address < dataseg){
stdata->state ^= SET;
getfmt(stdata,fstart); /* reset format */
}
else
fputs("Symbol not in code segment\n",stderr);
break;
case 'f':/*specify format string for subroutine arguments or */
/*external variable */
/*f <symbol> <format string> */
if(!(ste = getsym(&fstart))){
serr();
break;
}
stdata = (SYMTBL_ENTRY *)symdat(symtbl,ste);
if(!*fstart){
/* return space used by current format string*/
if(stdata->ptype){
free(stdata->ptype);
stdata->ptype = NULL;
}
}
else
/* set new format string from rest of command*/
getfmt(stdata, fstart);
break;
case 'g':/*continue execution skipping n breakpoints*/
/*g [<number of break points to be displayed>] */
i = sscanf(fstart, "%d", &bcnt);
if(i == EOF) bcnt = 0;
return;
case 't':/*toggle trace mode */
trace = 1 - trace;
fputs("trace o", stderr);
fputs(trace ? "n\n" : "ff\n", stderr);
break;
case 'c':/*process debug commands from a disk file */
/*c <filename> */
if(++fpdepth == MAXCOMDEPTH){ /* nested too deep? */
--fpdepth;
fputs("Comand file nested to deeply\n",stderr);
break;
}
/* get filename from rest of command line */
fname(fstart, ".DBG");
if(!(fp[fpdepth] = fopen(fstart, "r"))){
fprintf(stderr,
"Could not open file %s\n",fstart);
--fpdepth;
}
break;
case 'l':/*load file of symbols generated by link */
/*l <filename> */
/* get file name from rest of command line */
fname(fstart, ".SYM");
ldtbl(fstart);
break;
case 'd':/*display global variabk */
/*d <symbol name> */
/*d without args displays all external variables in*/
/* the data segment */
if(!*fstart){
for(stdata = alphafirst; stdata;
stdata = stdata->slalpha)
ddata("%s ",stdata->address,
stdata->ptype, ste, 1);
break;
}
if(!(ste = getsym(&fstart))){
serr();
break;
}
stdata = (SYMTBL_ENTRY *)symdat(symtbl, ste);
ddata("%s=",stdata->address,
getfmt(stdata, fstart),ste,1);
break;
case 'w':/*display argument stack */
i = depth;
for(depth = 0;depth < i;++depth){
dentry(bpstack[depth].sp,bpstack[depth].stdata);
}
depth = i;
break;
case 's':/*display or set symbol table entry */
/*s without arguments display all symbols */
/*s <symbol> displays symbol and address */
/*s <symbol> <hex> creates symbol with given address*/
if(!*fstart){
for(stdata = alphafirst; stdata;
stdata = stdata->slalpha)
dsymtbl(stdata);
break;
}
fend = fstart;
ste = getsym(&fend);
i = sscanf(fend, "%4x", &addr);
if(i == EOF) i = 0;
if(!i && ste)
dsymtbl((SYMTBL_ENTRY *)symdat(symtbl,ste));
else if(!i && !ste)
serr();
else if(i && ste){
stdata = (SYMTBL_ENTRY *)symdat(symtbl, ste);
stdata->address = addr;
dsymtbl(stdata);
}
else
loadsym(fstart,addr);
break;
default:
fputs("invalid debugger command\n", stderr);
case '\n':;
/*null command*/
}
}
}
/*************************************************************
dsymtbl - display data for one symbol table entry
**************************************************************/
static void
dsymtbl(stdata)
SYMTBL_ENTRY *stdata;
{
char c;
c = (stdata->state & SET) ? 'B' : ' ';
fprintf(stderr,
stdata->ptype ?
"%c %x\t%s\t%s\n" : "%c %x\t%s\n",
c, stdata->address, stdata->symbol, stdata->ptype);
}
/**************************************************************
relink - remove entry point from its current list and link on
to head of a new list.
**************************************************************/
static void
relink(stdata, list)
SYMTBL_ENTRY *stdata, **list;
{
/*delink from current breakpoint list */
if(stdata->slnxt)
stdata->slnxt->slprv = stdata->slprv;
stdata->slprv->slnxt = stdata->slnxt;
/*link into requested breakpoint list*/
if(*list)
(*list)->slprv = stdata;
stdata->slnxt = *list;
stdata->slprv = list;
*list = stdata;
}
/***************************************************************
actbp - activate trapping of a given entry point.
***************************************************************/
static void
actbp(stdata)
SYMTBL_ENTRY *stdata;
{
char *j;
/*load with "call trap" instruction */
j = stdata->address;
j[0] = 0xCD; /* Z-80 call */
j[1] = trap; /* "trap" address */
j[2] = trap >> 8;
/*followed by address of corresponding symbol table entry */
*((SYMTBL_ENTRY **)(j + INSTWIDTH)) = stdata;
}
/****************************************************************
dactbp - deactivate trapping of a given entry point.
*****************************************************************/
/* dactbp - deactivate a breakpoint */
static void
dactbp(stdata)
SYMTBL_ENTRY *stdata;
{
/* restore original contents of breakpoint address */
memcpy(stdata->address,stdata->saved_inst,
INSTWIDTH + sizeof(SYMTBL_ENTRY *));
}
/****************************************************************
fname - check and build name of file
****************************************************************/
static void
fname(prfix,sufix)
char *prfix, *sufix;
{
char c;
--prfix;
while((c = *++prfix) == ':' || isalnum(c));
if(*prfix != '.')
strmov(prfix, sufix);
else{
while(isalnum(*++prfix));
*prfix = '\0';
}
}
/*****************************************************************
getsym - get symbol from string. return pointer to next non
blank character.
*****************************************************************/
static SYMBOLENTRY *
getsym(text)
char **text;
{
char *i, *j;
int k;
if(!*text){
fputs("Comand needs symbol\n",stderr);
return NULL;
}
for(j = *text,k = 0;
*j && (!isspace(*j)) && (k < SYMSIZE);
++j,++k);
i = j;
while(!isspace(*j)) ++j; /*skip rest of current word */
while(isspace(*j)) ++j; /*skip to next word */
if(*(i - 1) == '?') --i; /*strip off trailing '?'*/
*i = '\0';
strlwr(*text);
i = *text;
*text = j;
return symlkup(symtbl, i);
}
/****************************************************************
serr - Symbol not defined message
*****************************************************************/
static void
serr()
{
fputs("Symbol not defined\n", stderr);
}
/****************************************************************
getfmt - get and/or store format to be displayed
*****************************************************************/
static char *
getfmt(stdata, fstart)
SYMTBL_ENTRY *stdata;
char *fstart;
{
char *p;
if(p = strchr(fstart, '\n'))
*p = NULL;
if(*fstart) {
p = stdata->ptype;
if(p)
free(p);
stdata->ptype =
p = malloc(1 + strlen(fstart));
strmov(p,fstart);
if(stdata->address < dataseg)
initp(stdata);
}
else if(!(p = stdata->ptype))
p = " %x";
return p;
}
/****************************************************************
initp - initialize format pointers for a code segment symbol
*****************************************************************/
static void
initp(stdata)
SYMTBL_ENTRY *stdata;
{
int arg, level;
char *p;
p = stdata->ptype;
/*separate first % as format for return value */
arg = FALSE;
level = 0;
for(;;){
switch(*p++){
case NULL:
case '\n':
break;
case '(':
++level;
continue;
case ')':
--level;
continue;
case '%':
if(arg && !level)
break;
else
arg = TRUE;
continue;
case '\t':
case ' ':
if(arg && !level){
--p;
break;
}
continue;
default:
continue;
}
break;
}
stdata->arg_format = p;
}
/************************************************************
ldtbl - load a symbol file from disk given its name
************************************************************/
static void
ldtbl(fname)
char *fname;
{
FILE *fl;
ADDRESS addr;
SYMBOLENTRY *ste;
if(!(fl = fopen(fname, "r"))){
fprintf(stderr,
"Could not open file %s\n",fname);
return;
}
while(fscanf(fl,"%x %s",&addr,format) == 2){
/* symbols starting with ? are generally*/
/* not interesting in programs compiled with */
/* QC. I have chosen to exclude them in */
/* to reduce size of symbol table */
if(format[0] == '?')
continue; /*skip internal symbols */
/* QC changes initial _ to @ to avoid */
/* error messages from linker */
if(format[0] == '@')
format[0] = '_';
fptr = format;
if(ste = getsym(&fptr)){
fprintf(stderr,
"%s already loaded\n",ste);
continue;
}
if(!loadsym(format, addr))
break;
}
fclose(fl);
}
/****************************************************************
loadsym - add a new symbol to symbol table
*****************************************************************/
static int
loadsym(symbol, addr)
char *symbol;
ADDRESS addr;
{
SYMBOLENTRY *ste;
SYMTBL_ENTRY *stdata;
if(!(ste = symadd(symtbl,symbol))){
fputs("Can't load any more symbols\n",
stderr);
return FALSE;
}
stdata = (SYMTBL_ENTRY *)symdat(symtbl, ste);
stdata->address = addr;
stdata->state =
stdata->ptype = NULL;
stdata->symbol = (char *)ste;
/*if in code segment add to entrylist */
if(addr < dataseg){
/*store in symbol table original contents of entry pt*/
memcpy(stdata->saved_inst,
addr,
INSTWIDTH + sizeof(SYMTBL_ENTRY *));
/*link into list of entry points*/
if(entrylist)
entrylist->slprv = stdata;
stdata->slnxt = entrylist;
entrylist = stdata;
stdata->slprv = &entrylist;
}
/*add to alphabetic list */
if(!alphafirst)
alphalast =
alphafirst = stdata;
else
alphalast->slalpha = stdata;
alphalast = stdata;
stdata->slalpha = NULL;
return TRUE;
}
/*******************************************************************
memcpy - copy n characters to destination from source
*********************************************************************/
static void
memcpy(dest, source, count)
char *dest, *source;
int count;
{
while(count--) *dest++ = *source++;
}
if(!alphafirst)
alphalast =
alphafirst = stdata;
e